#include "lib.h"

#include "tcpgame.h"
#include "tcpconn.h"
#include "shue.h"
#include "tcphand.h"
#include "cardmgr.h"
#include "tcpranks.h"
#include "tcpsaved.h"

#define THREECARDPOKER_NUMDECKS	1		// num decks in shue

typedef uint8 Outcomes;

static CardMgr stdMgr;

const static int32 sAnteBonusPayTable[MAX_AB_PAYFREQ][HR_STRAIGHTFLUSH + 1] =
{
    {   0, 0, 0, 1, 2, 3  },          //LOW ante bonus pay table

    {   0, 0, 0, 1, 3, 4  },          //MEDIUM ante bonus pay table (from the Aladdin)

    {   0, 0, 0, 1, 4, 5  },          //HIGH ante bonus pay table (normal)
};


const static int32 sPairPlusPayTable[MAX_PP_PAYFREQ][HR_STRAIGHTFLUSH + 1] =
{
    {   0, 1, 4, 6, 25, 35  },        // LOW pair plus pay table

    {   0, 1, 4, 6, 25, 40  },       // MEDIUM pair plus pay table (from the Aladdin)

    {   0, 1, 4, 6, 30, 40  }        // HIGH pair plus pay table (normal)
};


ThreeCardPokerGame::ThreeCardPokerGame(ThreeCardConnection* conn, int32 anteBet, int32 pairPlusBet)
{
	this->conn = conn;
	this->anteBet = anteBet;
    this->pairPlusBet = pairPlusBet;

	dealer = new ThreeCardPokerHand(&stdMgr);
	player = new ThreeCardPokerHand(&stdMgr);

    anteBonusPayTable = conn->getAnteBonusPayTable();
    pairPlusPayTable = conn->getSideBetPayTable();

	bGameOK = true;
}

ThreeCardPokerGame::ThreeCardPokerGame(ThreeCardConnection* conn, ThreeCardSavedGame* saved)
{
	this->conn = conn;

	// everything else is restored in restore()
	//
	anteBet = 0;
	pairPlusBet = 0;
	dealer = player = (ThreeCardPokerHand*)0;
    anteBonusPayTable = AB_PAYFREQ_LOW;
    pairPlusPayTable = PP_PAYFREQ_LOW;

	bGameOK = restore(saved);
}

ThreeCardPokerGame::~ThreeCardPokerGame()
{
	delete dealer;
	delete player;
}

// Rudy 12/206/042: Added this block to allow QA to test
#if (_DEBUG && MAGIC_DECK)

// Deck offsets (feel the thread-unsafeness)
int32 iPlayer = 0;
int32 iBanker = 0;



// Loaded player deck
static const uint8 puPlayerCards[] = {
	0x47, 0x46, 0x45,//1 straight flush
	0x47, 0x27, 0x17,//2 3 of kind
	0x27, 0x46, 0x35,//3 straight
	0x29, 0x27, 0x24,//4 flush
	0x3d, 0x2d, 0x15,//5 one pair
	0x3e, 0x4d, 0x2a	//6 high card
};

// Loaded dealer deck
static const uint8 puDealerCards[] = {
	0x4c, 0x39, 0x28,//1 straight flush
	0x3c, 0x4a, 0x17,//2 3 of kind
	0x2d, 0x19, 0x46,//3 straight
	0x4d, 0x2a, 0x37,//4 flush
	0x1e, 0x3c, 0x2d,//5 one pair
	0x4d, 0x2a, 0x18	//6 high card
};

// Get the next card from the player deck
uint8 getPlayerCard()
{
	uint8 uCard = puPlayerCards[iPlayer];
	if (++iPlayer >= sizeof(puPlayerCards)/sizeof(uint8))
		iPlayer = 0;
	return uCard;
}

// Get the next card from the dealer deck
uint8 getDealerCard()
{
	uint8 uCard = puDealerCards[iBanker];
	if (++iBanker >= sizeof(puDealerCards)/sizeof(uint8))
		iBanker = 0;
	return uCard;
}
#endif


bool8
ThreeCardPokerGame::deal(int32 balance)
{
	Shue* shue;
	bool8 result = false;

	ShueGameInfo sgi;
	sgi.gameId = TRICARD_POKER;
	sgi.machId = 0;
     #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif

	shue = new Shue(&stdMgr, THREECARDPOKER_NUMDECKS, sgi);

	if (shue && dealer && player)
	{
		shue->shuffle();

        // kharmon.  10-16-06.  #8226.  Save the deck seed, not the cards.
        ShueSave save;
        shue->save(&save);
        m_nDeckSeed = save.seed;

#if (_DEBUG && MAGIC_DECK)
		player->add(getPlayerCard());
		dealer->add(getDealerCard());  
		player->add(getPlayerCard());
		dealer->add(getDealerCard()); 
		player->add(getPlayerCard());
		dealer->add(getDealerCard());  
#else
		player->add(shue->getCard());
		dealer->add(shue->getCard());

		player->add(shue->getCard());
		dealer->add(shue->getCard());

		player->add(shue->getCard());
		dealer->add(shue->getCard());
#endif

		delete shue;

		if (conn->getDBInterface()->trackDeal(this))
		{
			conn->msgStart();
			conn->msgAdd((uint8)'D');
			conn->msgAdd((uint8)(player->getCount() & 0xff));
			conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
			conn->msgAdd(balance);
			conn->msgEnd();

            if (0 == anteBet && pairPlusBet > 0)
            {
                finish(' ');
            }

			result = true;
		}
	}

	return result;
}

bool8
ThreeCardPokerGame::raise()
{
	return finish('R');
}

bool8
ThreeCardPokerGame::fold()
{
	return finish('F');
}

bool8
ThreeCardPokerGame::finish(char raised)
{
	int32 payouts[3], balAdjust;
	int32 outcome;
	bool8 useHighCard;
	int32 balance;

	if (!calcPayouts(payouts, raised, &useHighCard, &outcome, &balAdjust))
		return false;

    // kharmon.  10-17-06.  #8226.  Extra parameter so that this call can save the cards from the hand.
	if (conn->getDBInterface()->trackGameDone(raised, payouts[0], payouts[1], payouts[2], outcome,
		                                      balAdjust, &balance, this))
	{
		conn->msgStart();
		conn->msgAdd((uint8)'F');
		conn->msgAdd((uint8)(dealer->getCount() & 0xff));
		conn->msgReleaseBuf(dealer->out(conn->msgGetBuf()));
		conn->msgAdd(payouts[0]);
		conn->msgAdd(payouts[1]);
		conn->msgAdd(payouts[2]);
		conn->msgReleaseBuf(dealer->outrank(conn->msgGetBuf()));
		conn->msgReleaseBuf(dealer->outhigh(conn->msgGetBuf()));
		conn->msgReleaseBuf(player->outrank(conn->msgGetBuf()));
		conn->msgReleaseBuf(player->outhigh(conn->msgGetBuf()));
		conn->msgAdd((uint8)(outcome & 0xff));
		conn->msgAdd((uint8)(useHighCard & 0xff));
		conn->msgAdd(balance);
		conn->msgEnd();

		conn->gameDone();	// delete "this"

		return true;
	}

	return false;
}

bool8
ThreeCardPokerGame::calcPayouts(int32* payouts, char raised,
	bool8* useHighCard, int32* outcome, int32* winnings)
{
    int32 dealerRank, playerRank;
    payouts[0] = 0;
    payouts[1] = 0;
    payouts[2] = 0;
	*winnings  = 0;
    *outcome = 0;

	//
	// Return dealer & player hand ranks & high card regardless of whether
	// player folded or not.  Although, not necessary when player folds, it's
	// nice to see what the dealer had (and how it would've ranked).
	//
	if (!dealer->rankHand())
		return false;
	dealer->calcNthHighest(0);

	if (!player->rankHand())
		return false;
	player->calcNthHighest(0);

    dealerRank = dealer->getRank();
    playerRank = player->getRank();

	*useHighCard = true;

    if (pairPlusBet > 0  && ((anteBet > 0 && 'R' == raised) || 0 == anteBet))
    {
        if (playerRank > HR_HIGHCARD)
            *outcome = ((*outcome) & 0xfc) | O_PLAYER_WON_SIDEBET;
        else
            *outcome = ((*outcome) & 0xfc) | O_PLAYER_LOST_SIDEBET;
    }

    if (anteBet > 0)
    {
       	if (dealerRank > playerRank)		// dealer wins: -ante, -raise
   	    	*outcome = ((*outcome) & 0x03) | O_DEALER_WON_ANTE;
        else if (dealerRank < playerRank)
    	    *outcome = ((*outcome) & 0x03) | O_PLAYER_WON_ANTE;
	    else
        {
	        int32 i, numCards;

		    *outcome = ((*outcome) & 0x03) | O_PUSH;
            numCards = dealer->getCount();

		    for (i = 0; i < numCards; i++)
    		{
	    		dealer->calcNthHighest(i);
		    	player->calcNthHighest(i);

	    		if (dealer->getHighCard() > player->getHighCard())
		    	{
			    	*outcome = ((*outcome) & 0x03) | O_DEALER_WON_ANTE;
				    break;
    			}
	    		else if (dealer->getHighCard() < player->getHighCard())
		    	{
			    	*outcome = ((*outcome) & 0x03) | O_PLAYER_WON_ANTE;
				    break;
    			}
		    }
	    }
        if ('R' == raised)
	    {
		    if (!dealer->qualifies())
       		    *outcome = ((*outcome) & 0x03) | O_DEALER_NOQUALIFY;
	    }
        else if ('F' == raised)
        {
    	    *outcome = ((*outcome) & 0x03) | O_PLAYER_FOLD;
        }
    }

    if (((*outcome) & O_PLAYER_WON_SIDEBET) && !((*outcome) & O_PLAYER_FOLD))
    {
        payouts[2] = pairPlusBet * sPairPlusPayTable[pairPlusPayTable][playerRank];
        *winnings += pairPlusBet + payouts[2];
    }
    else if (((*outcome) & O_PLAYER_LOST_SIDEBET) && !((*outcome) & O_PLAYER_FOLD))
    {
        payouts[2] = -pairPlusBet;
    }

    if ((*outcome) & O_DEALER_WON_ANTE)
    {
        payouts[0] = -anteBet;
        payouts[1] = -anteBet;
    }
    else if ((*outcome) & O_PLAYER_WON_ANTE)
    {
        payouts[0] = anteBet;
        payouts[1] = anteBet;
        *winnings += anteBet * 4;
    }
    else if ((*outcome) & O_PUSH)
    {
        *useHighCard = false;
        payouts[0] = 0;
        payouts[1] = 0;
        *winnings += anteBet * 2;
    }
    else if ((*outcome) & O_DEALER_NOQUALIFY)
    {
        payouts[0] = anteBet;
        payouts[1] = 0;
        *winnings += anteBet * 3;
    }
    else if ((*outcome) & O_PLAYER_FOLD)
    {
        payouts[0] = -anteBet;
        payouts[1] = 0;
        payouts[2] = -pairPlusBet;
        *winnings = 0;
    }

    if ((*outcome & O_DEALER_NOQUALIFY) |
        (*outcome & O_PUSH)             |
        (*outcome & O_PLAYER_WON_ANTE)  |
        (*outcome & O_DEALER_WON_ANTE))
    {
        if (playerRank >= HR_STRAIGHT)
        {
            int anteBonus = anteBet * sAnteBonusPayTable[anteBonusPayTable][playerRank];
            payouts[0] += anteBonus;
            *winnings += anteBonus;
        }
    }

	return true;
}

void
ThreeCardPokerGame::save(ThreeCardSavedGame* saved)
{
	// everything is already stored in the database (no need to store the shue)
}

bool8
ThreeCardPokerGame::restore(ThreeCardSavedGame* saved)
{
	anteBet = saved->anteBet;
	pairPlusBet = saved->sideBet;
    anteBonusPayTable = saved->anteBonusPayTable;
    pairPlusPayTable = saved->sideBetPayTable;

	dealer = new ThreeCardPokerHand(&stdMgr);
	player = new ThreeCardPokerHand(&stdMgr);	

    if(saved->bHasDeckSeed==true)
    {
        // kharmon.  10-17-06.  #8226.  This is a newer saved game that has a deckseed.
        // So, use that deckseed to regenerate the deck and redeal the cards for this hand.
	    ShueGameInfo sgi;
	    sgi.gameId = TRICARD_POKER;
	    sgi.machId = 0;

	    Shue *shue = new Shue(&stdMgr, THREECARDPOKER_NUMDECKS, sgi);

        ShueSave save;
        save.seed = saved->nDeckSeed;
        save.next = 0;
        shue->restore(&save);

		player->add(shue->getCard());
		dealer->add(shue->getCard());

		player->add(shue->getCard());
		dealer->add(shue->getCard());

		player->add(shue->getCard());
		dealer->add(shue->getCard());
    }
    else
    {
        // kharmon.  10-17-06.  #8226.  This is an older saved game that
        // has the cards stored in the save game record but no deckseed.  Just restore the cards.
        dealer->restore(saved->dealerCards, saved->numDealerCards);
        player->restore(saved->playerCards, saved->numPlayerCards);
    }

	//
	// At this point the server is *exactly* the same as when the game was
	// saved:  all automatic playing had already completed and the game
	// was idle.
	//

	// Make sure we have good hands
	return dealer->rankHand() && player->rankHand();
}

void
ThreeCardPokerGame::send()
{
	//
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
	//
	conn->msgAdd(anteBet);
	conn->msgAdd(pairPlusBet);
	conn->msgAdd(anteBonusPayTable);
	conn->msgAdd(pairPlusPayTable);
	conn->msgAdd((uint8)(player->getCount() & 0xff));
	conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
}
